﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

using CodeMvcApp.Models;

using IdentityModel.Client;

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

namespace CodeMvcApp.Controllers
{
    [Authorize]
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public async Task<IActionResult> Index()
        {
            HttpClient client = new HttpClient();
            DiscoveryDocumentResponse disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000/");
            if (disco.IsError)
            {
                throw new Exception(disco.Error);
            }

            string accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
            client.SetBearerToken(accessToken);

            HttpResponseMessage response = await client.GetAsync("http://localhost:6000/WeatherForecast");
            if (!response.IsSuccessStatusCode)
            {
                //判断如果响应的结果状态码是401 Unauthorized未授权
                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    //重新刷新令牌
                    await RenewTokenAsync();
                    //重定向到当前Action，即Home/Index
                    return RedirectToAction();
                }

                throw new Exception(response.ReasonPhrase);
            }

            string content = await response.Content.ReadAsStringAsync();
            return View("Index", content);
        }

        public async Task<IActionResult> Privacy()
        {
            string accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
            string idToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);

            string refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
            //code只能使用一次，所以获取不到
            //string code = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.Code);

            ViewData["accessToken"] = accessToken;
            ViewData["idToken"] = idToken;
            ViewData["refreshToken"] = refreshToken;

            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }

        public async Task Logout()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
        }

        /// <summary>
        /// 更新/刷新令牌
        /// </summary>
        /// <returns>访问令牌</returns>
        public async Task<string> RenewTokenAsync()
        {
            HttpClient client = new HttpClient();
            DiscoveryDocumentResponse disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000/");
            if (disco.IsError)
            {
                throw new Exception(disco.Error);
            }

            //获取刷新令牌
            string refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);

            //根据刷新令牌重新获取访问令牌
            TokenResponse tokenResponse = await client.RequestRefreshTokenAsync(new RefreshTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = "mvc client",
                ClientSecret = "mvc secret",
                Scope = "api1 openid profile email phone address",
                GrantType = OpenIdConnectGrantTypes.RefreshToken,
                RefreshToken = refreshToken
            });
            if (tokenResponse.IsError)
            {
                throw new Exception(tokenResponse.Error);
            }
            else
            {
                //重新计算过期时间（当前时间+token的有效期秒）
                var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResponse.ExpiresIn);
                //定义令牌集合，用于重新更新令牌
                var tokens = new[]
                {
                    //重新设置身份令牌
                    new AuthenticationToken
                    {
                        Name = OpenIdConnectParameterNames.IdToken,
                        Value = tokenResponse.IdentityToken
                    },
                    //重新设置访问令牌
                    new AuthenticationToken
                    {
                        Name = OpenIdConnectParameterNames.AccessToken,
                        Value = tokenResponse.AccessToken
                    },
                    //重新设置刷新令牌
                    new AuthenticationToken
                    {
                        Name = OpenIdConnectParameterNames.RefreshToken,
                        Value = tokenResponse.RefreshToken
                    },
                    //重新设置过期时间
                    new AuthenticationToken
                    {
                        Name = "expires_at",
                        Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
                    }
                };

                //获取身份认证的结果，包含当前的用户标识声明主体（Principal）+会话的其他状态值（Properties）
                var currentAuthenticateResult =
                    await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
                //将存储的token都重新更新一遍（将新的tokens存起来）
                currentAuthenticateResult.Properties.StoreTokens(tokens);

                //将当前身份认证结果（用户标识声明主体+会话的其他状态值）代入，重新执行登录动作
                await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
                    currentAuthenticateResult.Principal,//身份验证的用户标识的声明主体
                    currentAuthenticateResult.Properties//身份验证会话的其他状态值
                );

                return tokenResponse.AccessToken;
            }
        }
    }
}




















//using System;
//using System.Diagnostics;
//using System.Net.Http;
//using System.Threading.Tasks;

//using CodeMvcApp.Models;

//using IdentityModel.Client;

//using Microsoft.AspNetCore.Authentication;
//using Microsoft.AspNetCore.Authentication.Cookies;
//using Microsoft.AspNetCore.Authentication.OpenIdConnect;
//using Microsoft.AspNetCore.Authorization;
//using Microsoft.AspNetCore.Mvc;
//using Microsoft.Extensions.Logging;
//using Microsoft.IdentityModel.Protocols.OpenIdConnect;

//namespace CodeMvcApp.Controllers
//{
//    [Authorize]
//    public class HomeController : Controller
//    {
//        private readonly ILogger<HomeController> _logger;

//        public HomeController(ILogger<HomeController> logger)
//        {
//            _logger = logger;
//        }

//        public async Task<IActionResult> Index()
//        {
//            HttpClient client = new HttpClient();
//            DiscoveryDocumentResponse disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000/");
//            if (disco.IsError)
//            {
//                throw new Exception(disco.Error);
//            }

//            string accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
//            client.SetBearerToken(accessToken);

//            HttpResponseMessage response = await client.GetAsync("http://localhost:6000/WeatherForecast");
//            if (!response.IsSuccessStatusCode)
//            {
//                throw new Exception(response.ReasonPhrase);
//            }

//            string content = await response.Content.ReadAsStringAsync();
//            return View("Index", content);
//        }

//        public async Task<IActionResult> Privacy()
//        {
//            string accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
//            string idToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);

//            string refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
//            //string code = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.Code);

//            ViewData["accessToken"] = accessToken;
//            ViewData["idToken"] = idToken;
//            ViewData["refreshToken"] = refreshToken;

//            return View();
//        }

//        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
//        public IActionResult Error()
//        {
//            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
//        }

//        public async Task Logout()
//        {
//            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
//            await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
//        }
//    }
//}
